/*
 * ChartLayout.java
 *
 * Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 *  BEAST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.gui.chart;

import java.awt.*;

/**
 * A layout manager similar to BorderLayout but with specific regions for
 * parts of a chart (plot, axis, titles, etc.).
 *
 * @author 	Andrew Rambaut
 */

public class ChartLayout implements LayoutManager2 {
    int hgap;
    int vgap;

    Component title = null;
    Component xLabel = null;
    Component yLabel = null;
	Component chart = null;

    /**
     * Constructs a new ChartLayout.
     */
    public ChartLayout() {
		this(0, 0);
    }

    /**
     * Constructs a ChartLayout with the specified gaps.
     * @param hgap the horizontal gap
     * @param vgap the vertical gap
     */
    public ChartLayout(int hgap, int vgap) {
		this.hgap = hgap;
		this.vgap = vgap;
    }

    /**
     * Returns the horizontal gap between components.
     */
    public int getHgap() {
		return hgap;
    }

    /**
     * Sets the horizontal gap between components.
     * @param hgap the horizontal gap between components
     */
    public void setHgap(int hgap) {
		this.hgap = hgap;
    }

    /**
     * Returns the vertical gap between components.
     */
    public int getVgap() {
		return vgap;
    }

    /**
     * Sets the vertical gap between components.
     * @param vgap the vertical gap between components
     */
    public void setVgap(int vgap) {
		this.vgap = vgap;
    }

    /**
     * Adds the specified named component to the layout.
     * @param comp the component to be added
     */
    public void addLayoutComponent(Component comp, Object constraints) {
   		synchronized (comp.getTreeLock()) {
			if ((constraints == null) || (constraints instanceof String)) {
	    		addLayoutComponent((String)constraints, comp);
			} else {
	    		throw new IllegalArgumentException("cannot add to layout: constraint must be a string (or null)");
			}
		}
	}

    /**
     * @deprecated  replaced by <code>addLayoutComponent(Component, Object)</code>.
     */
    public void addLayoutComponent(String name, Component comp) {
		synchronized (comp.getTreeLock()) {
			/* Special case:  treat null the same as "Chart". */
			if (name == null) {
				name = "Chart";
			}

			if ("Chart".equals(name)) {
			    chart = comp;
			} else if ("Title".equals(name)) {
			    title = comp;
			} else if ("XLabel".equals(name)) {
			    xLabel = comp;
			} else if ("YLabel".equals(name)) {
			    yLabel = comp;
			} else {
				throw new IllegalArgumentException("cannot add to layout: unknown constraint: " + name);
			}
		}
    }

    /**
     * Removes the specified component from the layout.
     * @param comp the component to be removed
     */
    public void removeLayoutComponent(Component comp) {
		synchronized (comp.getTreeLock()) {
			if (comp == chart) {
			    chart = null;
			} else if (comp == title) {
			    title = null;
			} else if (comp == xLabel) {
			    xLabel = null;
			} else if (comp == yLabel) {
			    yLabel = null;
			}
		}
    }

    /**
     * Returns the minimum dimensions needed to layout the components
     * contained in the specified target container.
     * @param target the Container on which to do the layout
     * @see Container
     * @see #preferredLayoutSize
     */
    public Dimension minimumLayoutSize(Container target) {
		synchronized (target.getTreeLock()) {
			Dimension dim = new Dimension(0, 0);

			if ((chart != null) && chart.isVisible()) {
			    Dimension d = chart.getMinimumSize();
			    dim.width = d.width;
			    dim.height = d.height;
			}

			if ((xLabel != null) && xLabel.isVisible()) {
			    Dimension d = xLabel.getMinimumSize();
			    dim.width = Math.max(d.width, dim.width);
			    dim.height += d.height + vgap;
			}

			if ((yLabel != null) && yLabel.isVisible()) {
			    Dimension d = yLabel.getMinimumSize();
			    dim.width += d.width + hgap;
			    dim.height = Math.max(d.height, dim.height);
			}

			if ((title != null) && title.isVisible()) {
			    Dimension d = title.getMinimumSize();
			    dim.width = Math.max(d.width, dim.width);
			    dim.height += d.height + vgap;
			}

			Insets insets = target.getInsets();
			dim.width += insets.left + insets.right;
			dim.height += insets.top + insets.bottom;

			return dim;
		}
    }

    /**
     * Returns the preferred dimensions for this layout given the components
     * in the specified target container.
     * @param target the component which needs to be laid out
     * @see Container
     * @see #minimumLayoutSize
     */
    public Dimension preferredLayoutSize(Container target) {
		synchronized (target.getTreeLock()) {
			Dimension dim = new Dimension(0, 0);

			if ((chart != null) && chart.isVisible()) {
			    Dimension d = chart.getPreferredSize();
			    dim.width = d.width;
			    dim.height = d.height;
			}

			if ((xLabel != null) && xLabel.isVisible()) {
			    Dimension d = xLabel.getPreferredSize();
			    dim.width = Math.max(d.width, dim.width);
			    dim.height += d.height + vgap;
			}

			if ((yLabel != null) && yLabel.isVisible()) {
			    Dimension d = yLabel.getPreferredSize();
			    dim.width += d.width + hgap;
			    dim.height = Math.max(d.height, dim.height);
			}

			if ((title != null) && title.isVisible()) {
			    Dimension d = title.getPreferredSize();
			    dim.width = Math.max(d.width, dim.width);
			    dim.height += d.height + vgap;
			}

			Insets insets = target.getInsets();
			dim.width += insets.left + insets.right;
			dim.height += insets.top + insets.bottom;

			return dim;
		}
    }

    /**
     * Returns the maximum dimensions for this layout given the components
     * in the specified target container.
     * @param target the component which needs to be laid out
     */
    public Dimension maximumLayoutSize(Container target) {
		return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    /**
     * Returns the alignment along the x axis.  This specifies how
     * the component would like to be aligned relative to other
     * components.  The value should be a number between 0 and 1
     * where 0 represents alignment along the origin, 1 is aligned
     * the furthest away from the origin, 0.5 is centered, etc.
     */
    public float getLayoutAlignmentX(Container parent) {
		return 0.5f;
    }

    /**
     * Returns the alignment along the y axis.  This specifies how
     * the component would like to be aligned relative to other
     * components.  The value should be a number between 0 and 1
     * where 0 represents alignment along the origin, 1 is aligned
     * the furthest away from the origin, 0.5 is centered, etc.
     */
    public float getLayoutAlignmentY(Container parent) {
		return 0.5f;
    }

	/**
     * Invalidates the layout, indicating that if the layout manager
     * has cached information it should be discarded.
     */
    public void invalidateLayout(Container target) {
    }

	/**
     * Lays out the specified container. This method will actually reshape the
     * components in the specified target container in order to satisfy the
     * constraints of the CompassLayout object.
     * @param target the component being laid out
     * @see Container
     */
    public void layoutContainer(Container target) {
		synchronized (target.getTreeLock()) {
			Insets insets = target.getInsets();
			Dimension d;

			d=target.getSize();

			int top = insets.top;
			int bottom = d.height - insets.bottom;
			int left = insets.left;
			int right = d.width - insets.right;

			// defines the centre box.
			int top1=top;
			int bottom1=bottom;
			int left1=left;
			int right1=right;

			if ((xLabel != null) && xLabel.isVisible()) {
			    d = xLabel.getPreferredSize();
			    bottom1 -= d.height + vgap;
			}
			if ((yLabel != null) && yLabel.isVisible()) {
			    d = yLabel.getPreferredSize();
			    left1 += d.width + hgap;
			}
			if ((title != null) && title.isVisible()) {
			    d = title.getPreferredSize();
			    top1 += d.height + vgap;
			}


			if ((xLabel != null) && xLabel.isVisible())
			    xLabel.setBounds(left1, bottom1, right1 - left1, bottom - bottom1);

			if ((yLabel != null) && yLabel.isVisible())
			    yLabel.setBounds(left, top1, left1 - left, bottom1 - top1);

			if ((title != null) && title.isVisible())
			    title.setBounds(left, top, right - left, top1 - top);

			if ((chart != null) && chart.isVisible())
			    chart.setBounds(left1, top1, right1 - left1, bottom1 - top1);
		}
    }

    /**
     * Returns the String representation of this CompassLayout's values.
     */
    public String toString() {
		return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
    }
}
